/* ===== semantic.c ===== */
#include <string>
#include <iostream>
#include <map>
#include <list>
#include <vector>

using namespace std;

#include <stdio.h>
#include <stdlib.h>

#include "ptype.hh"
#include "symtab.hh"
#include "codegest.hh"

#include "myASTnode.hh"

#include "util.hh"
#include "semantic.hh"

#include "codegen.hh"

// symbol table with information about identifiers in the program
// declared in symtab.cc
extern symtab symboltable;

// When dealing with a list of instructions, it contains the maximum auxiliar space
// needed by any of the instructions for keeping non-referenceable non-basic-type values.
int maxoffsetauxspace;

// When dealing with one instruction, it contains the auxiliar space
// needed for keeping non-referenceable values.
int offsetauxspace;

// For distinghishing different labels for different if's and while's.
int newLabelWhile(bool inicialitzar = false){
  static int comptador = 1;
  if (inicialitzar) comptador = 0;
  return comptador++;
}

int newLabelIf(bool inicialitzar = false){
  static int comptador = 1;
  if (inicialitzar) comptador = 0;
  return comptador++;
}
/**********************************************************************/

codechain indirections(int jumped_scopes,int t)
{
  codechain c;
  if (jumped_scopes==0) {
    c="aload static_link t"+itostring(t);
  }
  else {
    c="load static_link t"+itostring(t);
    for (int i=1;i<jumped_scopes;i++) {
      c=c||"load t"+itostring(t)+" t"+itostring(t);
    }
  }
  return c;
}
/**********************************************************************/
int compute_size(ptype tp)
{
  if (isbasickind(tp->kind)) {
    tp->size=4;
  }
  else if (tp->kind=="array") {
    tp->size=tp->numelemsarray*compute_size(tp->down);
  }
  else if (tp->kind=="struct") {
    tp->size=0;
    for (list<string>::iterator it=tp->ids.begin();it!=tp->ids.end();it++) {
      tp->offset[*it]=tp->size;
      tp->size+=compute_size(tp->struct_field[*it]);
    }
  }
  return tp->size;
}
/**********************************************************************/
void gencodevariablesandsetsizes(scope *sc,codesubroutine &cs,bool isfunction=0)
{
  if (isfunction) cs.parameters.push_back("returnvalue");
  for (list<string>::iterator it=sc->ids.begin();it!=sc->ids.end();it++) {
    if (sc->m[*it].kind=="idvarlocal") {
      variable_data vd;
      vd.name="_"+(*it);
      vd.size=compute_size(sc->m[*it].tp);
      cs.localvariables.push_back(vd);
    } else if (sc->m[*it].kind=="idparval" || sc->m[*it].kind=="idparref") {
      compute_size(sc->m[*it].tp);
      cs.parameters.push_back("_"+(*it));
    } else if (sc->m[*it].kind=="idfunc") {
      // Here it is assumed that in tp->right is kept the return value type
      // for the case of functions. If this is not the case you must
      // change this line conveniently in order to force the computation
      // of the size of the type returned by the function.
      compute_size(sc->m[*it].tp->right);
    } else if (sc->m[*it].kind=="idproc") {
      // Nothing to be done.
    }
    //cout << sc->m[*it].kind << endl;
    
  }
  cs.parameters.push_back("static_link");
}
/**********************************************************************/
codechain GenLeft(AST *a,int t);
codechain GenRight(AST *a,int t);
/**********************************************************************/
void CodeGenRealParams(AST *a,ptype tp,codechain &cpushparam,codechain &cremoveparam,int t)
{
  if (!a) return;
  //cout<<"Starting with node \""<<a->kind<<"\""<<endl;
  //...to be done.
	while(a != NULL) {
		//parametre per referencia, generem l'adreça (GenLeft)
		if(tp->kind == "parref")
	 		cpushparam = cpushparam || GenLeft(a,t) || 
	 		"pushparam t"+itostring(t); 
	 	//parametre per valor de tipus basic, generem el valor (GenRight)
	 	else if(tp->kind == "parval" && isbasickind(tp->down->kind))
	 		cpushparam = cpushparam || GenRight(a,t) || "pushparam t"
	 		+ itostring(t); 
	 	//parametre per valor de tipus no basic referenciable, 
	 	//fem una copia a l'espai auxiliar 
		else if(a->ref == 1){
			cpushparam = cpushparam || GenLeft(a,t+1)
			  || "aload aux_space t" + itostring(t)
			  || "addi t" + itostring(t) +" " +itostring(offsetauxspace) 
			  +" t" + itostring(t)
			  || "copy t" +itostring(t+1) + " t" + itostring(t) + " " 
			  + itostring(compute_size(a->tp))
			  || "pushparam t" + itostring(t);
			
			offsetauxspace += compute_size(a->tp);
		}
		//parametre per valor de tipus no basic no referenciable
		//(retorn d'una funcio), generem l'adreça (GenLeft)
		else {
			cpushparam = cpushparam || GenRight(a,t)
			  || "pushparam t" + itostring(t);
			
		}
		cremoveparam = cremoveparam || "killparam";
		a = a->right;
		tp = tp->right;
	}
  //cout<<"Ending with node \""<<a->kind<<"\""<<endl;
}
/**********************************************************************/
// ...to be completed:
codechain GenLeft(AST *a,int t)
{
  codechain c;

  if (!a) {
    return c;
  }

  //cout<<"Starting with node \""<<a->kind<<"\""<<endl;
/*-------------------------------------------------------------------*/  
  if (a->kind=="ident") {    
    //la variable es troba a l'ambit actual
    if(symboltable.jumped_scopes(a->text)==0){
		//parametre per referencia, o per valor i de tipus no basic, 
		//obtenim el valor (que es l'adreça)
    	if(symboltable[a->text].kind == "idparref" || 
    	  (symboltable[a->text].kind == "idparval" && 
    	  !isbasickind(symboltable[a->text].tp->kind))) { 
			c="load _"+a->text+" t"+itostring(t);
	    }
		//parametre per valor de tipus basic o variable local, 
		//obtenim l'adreça
		else if(symboltable[a->text].kind == "idparval" || 
				symboltable[a->text].kind == "idvarlocal"){
					c="aload _"+a->text+" t"+itostring(t);
		}
    }
  	//la variable es troba en un ambit superior
  	else {
	  	//obtenim el static_link corresponent a l'ambit on es 
	  	//troba la variable, i sumem l'offset d'aquesta
	  	c = indirections(symboltable.jumped_scopes(a->text),t) ||
	  	"addi t" + itostring(t) + " offset(" + 
	  	symboltable.idtable(a->text) + ":_" + a->text + ") t" + 
	  	itostring(t);
	  	//parametre per referencia o per valor i de tipus no basic, 
	  	//obtenim el valor (que es l'adreça)
      	if(symboltable[a->text].kind == "idparref" || 
      	  (symboltable[a->text].kind == "idparval" && 
      	  !isbasickind(symboltable[a->text].tp->kind))){
	  		c = c ||"load t" + itostring(t) + " t" + itostring(t);
  	  	}
  	}
  }
/*-------------------------------------------------------------------*/  
  else if (a->kind=="."){
    //si el primer fill es referenciable, obtenim l'adreça (GenLeft)
    if(child(a,0)->ref == 1)c=GenLeft(child(a,0),t);
    //si no es referenciable (retorn d'una funcio), fem GenRight
    else c = GenRight(child(a,0),t);
    //afegim l'offset dins l'struct
    c=  c || "addi t"+itostring(t)+" "+
    itostring(child(a,0)->tp->offset[child(a,1)->text])+" t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
else if(a->kind=="[") {
	//obtenim l'adreça base (GenLeft), despres la posicio a la que volem 
	//accedir (GenRight), multipliquem pel size del tipus de l'array.
	//Finalment sumem el desplaçament corresponent a l'adreça base
	c=GenLeft(child(a,0),t) || 
	GenRight(child(a,1),t+1) ||
	"muli t"+itostring(t+1)+" "+
	itostring((child(a,0)->tp->size)/(child(a,0)->tp->numelemsarray))
	+" t"+itostring(t+1) ||
	"addi t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
		
}
/*-------------------------------------------------------------------*/
  else {
    cout<<"BIG PROBLEM! No case defined for kind "<<a->kind<< "line " << a->line <<endl;
  }
  //cout<<"Ending with node \""<<a->kind<<"\""<<endl;
  return c;
}
/**********************************************************************/

// ...to be completed:
codechain GenRight(AST *a,int t)
{
  codechain c;

  if (!a) {
    return c;
  }
/*-------------------------------------------------------------------*/
  //cout<<"Starting with node \""<<a->kind<<"\""<<endl;
  //si es referenciable
  if (a->ref) {
	//variable local o parametre per valor, de tipus basic
	//a l'ambit actual, obtenim el valor
    if (a->kind=="ident" && symboltable.jumped_scopes(a->text)==0 &&
	     isbasickind(symboltable[a->text].tp->kind) && 
	     symboltable[a->text].kind!="idparref") {
			 c="load _"+a->text+" t"+itostring(t);
    }
    //variable o parametre de tipus basic, obtenim el valor
    else if (isbasickind(a->tp->kind)) {
    	c=GenLeft(a,t)||"load t"+itostring(t)+" t"+itostring(t);
    }
    //variable o parametre de tipus no basic, obtenim l'adreça
    else {//...to be done
		c = GenLeft(a,t);
    }    
  } 
/*-------------------------------------------------------------------*/
  else if (a->kind=="intconst") {
    c="iload "+a->text+" t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
  else if (a->kind == "true") {
	c = "iload 1 t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
  else if (a->kind == "false") {
	c = "iload 0 t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
  else if (a->kind=="+") {
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "addi t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
  else if (a->kind=="-" && child(a,1)!=0) {
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "subi t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
  else if (a->kind=="*") {
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "muli t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
  else if (a->kind=="/") {
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "divi t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
else if(a->kind == "-"){
	c=GenRight(child(a,0),t) ||
	"mini t"+itostring(t)+" t"+itostring(t);
}
/*-------------------------------------------------------------------*/
else if(a->kind == "and"){
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "land t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
}
/*-------------------------------------------------------------------*/
else if(a->kind == "or"){
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "loor t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
}
/*-------------------------------------------------------------------*/

else if(a->kind == "not"){
	c=GenRight(child(a,0),t) ||
	"lnot t"+itostring(t)+" t"+itostring(t);
}
/*-------------------------------------------------------------------*/
else if(a->kind == ">"){
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "grti t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
}
/*-------------------------------------------------------------------*/
else if(a->kind == "<"){
    c=GenRight(child(a,0),t)||
      GenRight(child(a,1),t+1)||
      "lesi t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
}
/*-------------------------------------------------------------------*/
else if(a->kind == "=") {
	c=GenRight(child(a,0),t)||
	GenRight(child(a,1),t+1)||
	"equi t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
	
	
}
/*-------------------------------------------------------------------*/
//funcio
else if(a->kind == "(") {
	//primer parametre
	AST *aux = child(child(a,1),0);
	int i = 1;
 	ptype tp = symboltable[child(a,0)->text].tp;
 	ptype tp2 = tp;
 	codechain cpushparam;
 	codechain cremoveparam;
 	int taux;
 	
	if(child(a,0)->tp->kind == "function"){
		//retorn de tipus basic
		if(isbasickind(tp2->right->kind)) c = "pushparam 0";
		//retorn de tipus no basic
		else{
			c =   "aload aux_space t" + itostring(t)
				  || "addi t" + itostring(t) +" " +
				  itostring(offsetauxspace) +" t" + itostring(t)
				  || "pushparam t" + itostring(t);
		 	taux = t;
			++t;
			offsetauxspace += compute_size(tp2->right);
		}
		
	}
 	tp = tp->down;
	CodeGenRealParams(aux,tp,cpushparam,cremoveparam,t);
	c = c || cpushparam 
		|| indirections(symboltable.jumped_scopes(child(a,0)->text),t) 
		|| "pushparam t" + itostring(t)
		|| "call program_" + child(a,0)->text || "killparam"
		|| cremoveparam;

	if(child(a,0)->tp->kind == "function") {
		//retorn de tipus basic, deixem a t el resultat
		if(isbasickind(tp2->right->kind)) 
			c = c || "popparam t" + itostring(t);
		//retorn de tipus no basic, nomes hem de fer el 
		//killparam de l'auxspace
		else {
			c = c || "killparam";
		}

	}
	
}
/*-------------------------------------------------------------------*/
  else if (a->kind=="."){
	compute_size(child(a,0)->tp);
    c=  GenRight(child(a,0),t) || "addi t"+itostring(t)+" "+
    itostring(child(a,0)->tp->offset[child(a,1)->text])+" t"+itostring(t);
  }
/*-------------------------------------------------------------------*/
else if(a->kind=="[") {
	c=GenRight(child(a,0),t) || 
	GenRight(child(a,1),t+1) ||
	"muli t"+itostring(t+1)+" "+
	itostring((child(a,0)->tp->size)/(child(a,0)->tp->numelemsarray))
	+" t"+itostring(t+1) ||
	"addi t"+itostring(t)+" t"+itostring(t+1)+" t"+itostring(t);
		
}

/*-------------------------------------------------------------------*/
  else {
    cout<<"BIG PROBLEM! No case defined for kind "<<a->kind<< "line " << a->line << endl;
  }
  //cout<<"Ending with node \""<<a->kind<<"\""<<endl;
  return c;
}
/**********************************************************************/

// ...to be completed:
codechain CodeGenInstruction(AST *a,string info="")
{
  codechain c;

  if (!a) {
    return c;
  }
  //cout<<"Starting with node \""<<a->kind<<"\""<<endl;
  offsetauxspace=0;
  if (a->kind=="list") {
    for (AST *a1=a->down;a1!=0;a1=a1->right) {
      c=c||CodeGenInstruction(a1,info);
    }
  }
/*-------------------------------------------------------------------*/
  else if (a->kind==":=") {
    //tipus basic
    if (isbasickind(child(a,0)->tp->kind)) {
        c=GenLeft(child(a,0),0)||GenRight(child(a,1),1)||"stor t1 t0";
    }
    //tipus no basic, hem de fer una copia
    else {
      c=GenLeft(child(a,0),0)||GenRight(child(a,1),1)||"copy t1 t0 "+ 
      itostring(compute_size(child(a,1)->tp));
    }
  } 
/*-------------------------------------------------------------------*/
  else if (a->kind=="write" || a->kind=="writeln") {
    if (child(a,0)->kind=="string") {
      //...to be done.
	  c = "wris " + child(a,0)->text;
    } 
    else {//Exp
		c=GenRight(child(a,0),0)||"wrii t0";
    }

    if (a->kind=="writeln") {
      c=c||"wrln";
    }
  }

/*-------------------------------------------------------------------*/
else if(a->kind == "if") {
	int n = newLabelIf();
	//si hi ha else
	if(child(a,2)) {
		//expressio booleana
		c= GenRight(child(a,0),0)||
		"fjmp t0 else_"+itostring(n) ||
		//instruccions de l'if
		CodeGenInstruction(child(a,1))||
		"ujmp endif_"+itostring(n)||
		"etiq else_"+itostring(n) ||
		//instruccions de l'else
		CodeGenInstruction(child(a,2))||
		"etiq endif_"+itostring(n);
	}
	//si no hi ha else       
	else {
		//expressio booleana
		c= GenRight(child(a,0),0)||
		"fjmp t0 endif_"+itostring(n) ||
		//instruccions de l'if
		CodeGenInstruction(child(a,1))||
		"etiq endif_"+itostring(n);
	}
}

/*-------------------------------------------------------------------*/
else if(a->kind == "while") {
	int n = newLabelWhile();
    c ="etiq while_"+itostring(n) ||
    //expressio booleana
	GenRight(child(a,0),0)||
	"fjmp t0 endwhile_"+itostring(n) ||
	//instruccions del while
	CodeGenInstruction(child(a,1)) ||
	"ujmp while_"+itostring(n) ||
	"etiq endwhile_"+itostring(n);
	
}
/*-------------------------------------------------------------------*/
//procediment
else if(a->kind == "(") {
	//primer parametre
	AST *aux = child(child(a,1),0);
 	ptype tp = symboltable[child(a,0)->text].tp->down; 	
 	codechain cpushparam;
 	codechain cremoveparam;
	
	CodeGenRealParams(aux,tp,cpushparam,cremoveparam,0);
	c = c || cpushparam || 
		indirections(symboltable.jumped_scopes(child(a,0)->text),0) || 
		"pushparam t0" || "call " + 
		symboltable.idtable(child(a,0)->text)+"_" + child(a,0)->text || 
		"killparam" || cremoveparam;

	
}

/*-------------------------------------------------------------------*/
//si cal, actualitzem el maxauxspace
if(offsetauxspace > maxoffsetauxspace) maxoffsetauxspace = offsetauxspace;

  //cout<<"Ending with node \""<<a->kind<<"\""<<endl;

  return c;
}
/**********************************************************************/
void CodeGenSubroutine(AST *a,list<codesubroutine> &l)
{
  codesubroutine cs;

  //cout<<"Starting with node \""<<a->kind<<"\""<<endl;
  string idtable=symboltable.idtable(child(a,0)->text);
  cs.name=idtable+"_"+child(a,0)->text;
  symboltable.push(a->sc);
  symboltable.setidtable(idtable+"_"+child(a,0)->text);

  //...to be done.
  //primer parametre (val o ref)
  AST *aux = child(child(child(a,0),0),0);

  if(a->tp->kind == "procedure") gencodevariablesandsetsizes(a->sc,cs,0);
  else  gencodevariablesandsetsizes(a->sc,cs,1);
  //blocs
  for (AST *a1=child(child(a,2),0);a1!=NULL;a1=a1->right) {
    CodeGenSubroutine(a1,l);
  }
  
  newLabelIf(true);
  newLabelWhile(true);
  maxoffsetauxspace=0;

  if(a->tp->kind == "procedure") 
  	cs.c = CodeGenInstruction(child(a,3),a->tp->kind) || "retu";
  else if(a->tp->kind == "function") {
    //retorn de tipus basic
  	if(isbasickind(child(a,4)->tp->kind)) {
	  	cs.c = CodeGenInstruction(child(a,3),a->tp->kind) || 
	  	GenRight(child(a,4),0)||
	  	"stor t0 returnvalue" ||
	  	"retu";
	 }
	 //retorn de tipus no basic
	 else {
	 	cs.c = CodeGenInstruction(child(a,3),a->tp->kind) || 
	  	GenLeft(child(a,4),1)||
	  	"load returnvalue t0" ||
	  	"copy t1 t0 " + itostring(compute_size(child(a,4)->tp)) ||
	  	"retu";
	 }
  }

 if (maxoffsetauxspace>0) {
    variable_data vd;
    vd.name="aux_space";
    vd.size=maxoffsetauxspace;
    cs.localvariables.push_back(vd);
  }

  symboltable.pop();
  l.push_back(cs);
  //cout<<"Ending with node \""<<a->kind<<"\""<<endl;

}
/**********************************************************************/
void CodeGen(AST *a,codeglobal &cg)
{
  initnumops();
  securemodeon();
  cg.mainsub.name="program";
  symboltable.push(a->sc);
  symboltable.setidtable("program");
  gencodevariablesandsetsizes(a->sc,cg.mainsub);
  for (AST *a1=child(child(a,1),0);a1!=0;a1=a1->right) {
    CodeGenSubroutine(a1,cg.l);
  }
  maxoffsetauxspace=0; newLabelIf(true); newLabelWhile(true);
  cg.mainsub.c=CodeGenInstruction(child(a,2))||"stop";
  if (maxoffsetauxspace>0) {
    variable_data vd;
    vd.name="aux_space";
    vd.size=maxoffsetauxspace;
    cg.mainsub.localvariables.push_back(vd);
  }
  symboltable.pop();
}

